package br.com.java.dao.impl;

import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.List;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Transactional;

import br.com.java.dao.GenericDAO;

@SuppressWarnings("unchecked")
public class GenericDAOImpl<ENTITY, TYPE> implements GenericDAO<ENTITY, TYPE> {

	private static final long serialVersionUID = 1L;

	private static Logger LOGGER = Logger.getLogger("GenericDao");
	
	//	
	/** The persistent class. */
	protected Class<ENTITY> entityClass;
	
	/** SessionFactory. */	
	protected SessionFactory sessionFactory;
	
	/** HibernateTemplate. */	
	HibernateTemplate hibernateTemplate;
	
	/**
	 * Instantiates a new resource dao hibernate impl.
	 */
	public GenericDAOImpl() {
        this.entityClass = (Class<ENTITY>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
		
	@Autowired
	public void setSessionFactory(SessionFactory sessionFactory){
		this.sessionFactory = sessionFactory;
		this.hibernateTemplate = new HibernateTemplate(sessionFactory);
	}

    /**
     * Gets the persistent class.
     * 
     * @return the persistent class
     */
    public Class<ENTITY> getEntityClass() 
    {
    	return entityClass;
	}
    
    /**
     * Insere ou Atualiza um Objeto.
     * 
     * @param object the object
     */	
    @Transactional
    public void saveOrUpdate(ENTITY object)
    {
    	try {
    		sessionFactory.getCurrentSession().saveOrUpdate(object);
    		LOGGER.debug("saveOrUpdate successful");
		} catch (HibernateException  e) {
			LOGGER.error("saveOrUpdate failed", e);
			throw e;
		}
    }
          
    /**
     * Insere um Objeto.
     * 
     * @param object the object
     */	
    @Transactional
	public void save(ENTITY object) 
	{
    	try{
    		sessionFactory.getCurrentSession().save(object);
    		LOGGER.debug("save successful");
		} catch (HibernateException  e) {
			LOGGER.error("save failed", e);
			throw e;
		}
	}
	
	/**
	 * Insere vários Objetos.
	 * 
	 * @param collection the objects
	 */
	@Transactional
	public void saveAll(Collection<ENTITY> collection) 
	{
		for (ENTITY object : collection) 
		{
			save(object);
		}		
	}

	/**
	 * Atualiza um Objeto.
	 * 
	 * @param object the object
	 */	
	@Transactional
	public void update(ENTITY object) 
	{
		try{
			sessionFactory.getCurrentSession().update(object);
			LOGGER.debug("update successful");
		} catch (HibernateException  e) {
			LOGGER.error("update failed", e);
			throw e;
		}
	}
	
	/**
	 * Atualiza vários Objetos.
	 * 
	 * @param collection the objects
	 */	
	@Transactional
	public void updateAll(Collection<ENTITY> collection) 
	{
		for (ENTITY object : collection) 
		{
			update(object);
		}		
	}
	
    /**
     * Remove um Objeto.
     * 
     * @param object the object
     */		
	@Transactional
	public void remove(ENTITY object) 
	{
		try{
			sessionFactory.getCurrentSession().delete(object);
			LOGGER.debug("delete successful");
		} catch (HibernateException  e) {
			LOGGER.error("delete failed", e);
			throw e;
		}
	}
	
	/**
	 * Busca um Objeto por Id.
	 * 
	 * @param id the id
	 * 
	 * @return the TYPE
	 */
	@Transactional(readOnly=true)
    public ENTITY retrieveById(Long id) 
    {
		ENTITY entity;
		try{
			entity = (ENTITY)sessionFactory.getCurrentSession().get(getEntityClass(), id);
			LOGGER.debug("saveOrUpdate successful");
		} catch (HibernateException  e) {
			LOGGER.error("saveOrUpdate failed", e);
			throw e;
		}
		return entity;
    }		
	
	/**
	 * Busca um Objeto por Exemplos.
	 * 
	 * @param exemplo the exemplo
	 * 
	 * @return the list<type>
	 */	
	@Transactional(readOnly=true)
	public List<ENTITY> retrieveByExample(ENTITY exemplo) 
	{	try{
			return (List<ENTITY>)hibernateTemplate.findByExample(exemplo);
		} catch (DataAccessException  e) {
			LOGGER.error("findByExample failed", e);
			throw e;
		}
	}
	
	/**
	 * Busca um Objeto por Exemplos.
	 * 
	 * @return the list<type>
	 */	
	@Transactional(readOnly=true)
	public List<ENTITY> retrieveAll() 
	{
		return (List<ENTITY>)hibernateTemplate.loadAll(getEntityClass());		
	}
}
